//! Variable and constant declaration parsers

use super::super::{parse_expression, parse_type, Parser};
use crate::ast::decl::*;
use crate::error::{CompilerError, Result};
use crate::lexer::token::Token;

/// Parse variable declaration
pub fn parse_variable_declaration(parser: &mut Parser) -> Result<VariableDecl> {
    parser.expect(&Token::Var)?;

    if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let var_name = name.clone();
        parser.advance()?;

        // Type is REQUIRED (Go style: var name type)
        let var_type = parse_type(parser)?;

        if var_type.is_none() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Type declaration is required. Use: var name type = value",
            ));
        }

        // Optional initializer
        let initializer = if parser.consume(&Token::Assign)? {
            parse_expression(parser)?
        } else {
            None
        };

        parser.expect(&Token::Semicolon)?;

        Ok(VariableDecl {
            name: var_name,
            var_type,
            initializer,
            mutable: true, // Variables are mutable by default in NRC
            location: parser.current_location(),
        })
    } else {
        Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected variable name after 'var'",
        ))
    }
}

/// Parse constant declaration
pub fn parse_constant_declaration(parser: &mut Parser) -> Result<ConstantDecl> {
    parser.expect(&Token::Const)?;

    if let Some(Token::Identifier(name)) = parser.peek().map(|t| &t.token) {
        let const_name = name.clone();
        parser.advance()?;

        let const_type = if parser.consume(&Token::Colon)? {
            parse_type(parser)?
        } else {
            None
        };

        parser.expect(&Token::Assign)?;

        let value = parse_expression(parser)?;
        if value.is_none() {
            return Err(CompilerError::syntax(
                parser.current_location().line,
                parser.current_location().column,
                "Expected value after '='",
            ));
        }

        parser.expect(&Token::Semicolon)?;

        Ok(ConstantDecl {
            name: const_name,
            const_type,
            value: value.unwrap(),
            location: parser.current_location(),
        })
    } else {
        Err(CompilerError::syntax(
            parser.current_location().line,
            parser.current_location().column,
            "Expected constant name after 'const'",
        ))
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::parser::Parser;

    fn parse_var_decl(input: &str) -> Result<VariableDecl> {
        let mut parser = Parser::new(input.to_string(), None);
        parser.advance()?;
        parse_variable_declaration(&mut parser)
    }

    fn parse_const_decl(input: &str) -> Result<ConstantDecl> {
        let mut parser = Parser::new(input.to_string(), None);
        parser.advance()?;
        parse_constant_declaration(&mut parser)
    }

    #[test]
    fn test_parse_variable_declaration_with_type() {
        let result = parse_var_decl("var x int;");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "x");
        assert!(decl.var_type.is_some());
    }

    #[test]
    fn test_parse_variable_declaration_with_type_and_value() {
        let result = parse_var_decl("var x int = 42;");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "x");
        assert!(decl.var_type.is_some());
        assert!(decl.initializer.is_some());
    }

    #[test]
    fn test_parse_variable_declaration_no_type_error() {
        let result = parse_var_decl("var x;");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_variable_declaration_no_name_error() {
        let result = parse_var_decl("var ;");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_constant_declaration() {
        let result = parse_const_decl("const PI = 3.14;");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "PI");
        assert!(decl.const_type.is_none());
        assert!(matches!(
            decl.value,
            crate::ast::expr::Expression::Literal(_)
        ));
    }

    #[test]
    fn test_parse_constant_declaration_with_type() {
        // NRC const syntax: const NAME: TYPE = value or const NAME = value
        let result = parse_const_decl("const PI: float = 3.14;");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "PI");
        assert!(decl.const_type.is_some());
    }

    #[test]
    fn test_parse_constant_declaration_no_value_error() {
        let result = parse_const_decl("const PI =;");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_constant_declaration_no_name_error() {
        let result = parse_const_decl("const = 3.14;");
        assert!(result.is_err());
    }

    #[test]
    fn test_parse_variable_declaration_string_type() {
        let result = parse_var_decl("var name string = \"hello\";");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "name");
    }

    #[test]
    fn test_parse_variable_declaration_bool_type() {
        let result = parse_var_decl("var flag bool = true;");
        assert!(result.is_ok());
        let decl = result.unwrap();
        assert_eq!(decl.name, "flag");
    }
}
